package edu.uoc.vmerinom.tfc.action;

import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.faces.model.SelectItem;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.log.Log;

import edu.uoc.vmerinom.tfc.domain.Asistente;
import edu.uoc.vmerinom.tfc.domain.Reunion;
import edu.uoc.vmerinom.tfc.domain.Usuario;
import edu.uoc.vmerinom.tfc.service.ReunionesService;
import edu.uoc.vmerinom.tfc.support.Mail;
import edu.uoc.vmerinom.tfc.support.Message;
import edu.uoc.vmerinom.tfc.support.Utils;

/**
 * Manejador que contiene las acciones relacionadas con las reuniones y las
 * actas.
 * 
 * @author Víctor Manuel Merino Martínez
 * 
 */
@Name("reunionesAction")
@Scope(ScopeType.CONVERSATION)
public class ReunionesAction {
	/**
	 * Default Serial Version ID.
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * Log para la trazabilidad de errores.
	 */
	@Logger
	Log log;
	/**
	 * Almacena la fecha de hoy.
	 */
	private Date hoy;
	/**
	 * Reunión seleccionada.
	 */
	Reunion reunion;
	/**
	 * Hora de la reunión.
	 */
	String hora;
	/**
	 * Minuto de la reunión.
	 */
	String minuto;
	/**
	 * Listado de reuniones.
	 */
	List<Reunion> reuniones;
	/**
	 * Listado de horas;
	 */
	List<SelectItem> horas;
	/**
	 * Listado de minutos.
	 */
	List<SelectItem> minutos;
	/**
	 * Listado de usuarios.
	 */
	List<Usuario> usuarios;
	/**
	 * Listados de asistente anteriores.
	 */
	List<Asistente> anteriores;
	/**
	 * Listados de asistentes eliminados.
	 */
	List<Asistente> eliminados;
	/**
	 * Listado de usuarios asistentes.
	 */
	List<Usuario> asistentes;
	/**
	 * Mapa de usuarios seleccionados.
	 */
	Map<Usuario, Boolean> seleccionados;
	/**
	 * Servicio de reuniones.
	 */
	@In(create = true)
	ReunionesService reunionesService;
	/**
	 * Lógico que indica si se mostrará el listado de reuniones.
	 */
	boolean listarReuniones;
	/**
	 * Lógico que indica si se mostrará la edición de reunión.
	 */
	boolean editarReunion;

	/**
	 * Usuario autenticado.
	 */
	@In
	Usuario usuario;

	/**
	 * Carga los datos necesarios antes de mostrar la pantalla inicial.
	 */
	public void load() {
		if (horas == null || horas.isEmpty()) {
			horas = Utils.getHoras();
		}
		if (minutos == null || minutos.isEmpty()) {
			minutos = Utils.getMinutos();
		}
		if (usuario.getPerfil().isAdministrador()) {
			reuniones = reunionesService.obtenerReuniones();
		} else {
			reuniones = reunionesService.obtenerReunionesActivas();
		}

		for (Reunion reunion : reuniones) {
			reunionesService.actualizarEstadoReunion(reunion);
		}

		listarReuniones = true;
		editarReunion = false;
		seleccionados = new HashMap<Usuario, Boolean>();
		asistentes = new ArrayList<Usuario>();
		eliminados = new ArrayList<Asistente>();
		anteriores = new ArrayList<Asistente>();
		minuto = "00";
		hora = "00";
		hoy = new Date();
	}

	/**
	 * Añade al usuario como asistente a la reunión o lo elimina según el caso.
	 * 
	 * @param user
	 *            Usuario
	 */
	public void checkUsuario(Usuario user) {
		Asistente anterior = asistenteAnterior(user);
		if (seleccionados.get(user)) {
			if (anterior == null) {
				asistentes.add(user);
			} else {
				eliminados.remove(anterior);
			}
		} else {
			asistentes.remove(user);
			if (anterior != null) {
				eliminados.add(anterior);
			}
		}
	}

	/**
	 * Busca entre los asistentes que previamente tenía la reunión al usuario
	 * que recibe por parámetros. Si no lo encuentra devuelve <code>null</code>.
	 * 
	 * @param user
	 *            Usuario a buscar entre los asistentes
	 * @return Asistente encontrado o <code>null</code> en caso contrario
	 */
	private Asistente asistenteAnterior(Usuario user) {
		Asistente encontrado = null;
		if (!anteriores.isEmpty()) {
			Iterator<Asistente> iterator = anteriores.iterator();
			while (encontrado == null && iterator.hasNext()) {
				Asistente asistente = iterator.next();
				if (asistente.getUsuario().getId().equals(user.getId())) {
					encontrado = asistente;
				}
			}

		}
		return encontrado;
	}

	/**
	 * Carga los datos para volver a la pantalla del listado de reuniones.
	 */
	public void volver() {
		reunionesService.actualizarEstadoReunion(reunion);
		load();
	}

	/**
	 * Envía un correo a todos los posibles socios que puedan asistir, es decir,
	 * a los que estén activos en la fecha de la reunión.
	 */
	public void convocar() {
		List<Usuario> convocados = reunionesService.obtenerUsuarios(reunion.getFecha());
		if (convocados != null && !convocados.isEmpty()) {
			List<String> recipients = new ArrayList<String>();
			for (Usuario usuario : convocados) {
				log.info("To: " + usuario.getEmail());
				recipients.add(usuario.getEmail());
			}

			SimpleDateFormat fecha = new SimpleDateFormat("dd/MM/yyyy");
			SimpleDateFormat hora = new SimpleDateFormat("HH:mm");
			String subject = MessageFormat.format(Message.get("mail.meeting.subject"), new Object[] { fecha.format(reunion.getFecha()) });
			String message = MessageFormat.format(Message.get("mail.meeting.message"),
					new Object[] { fecha.format(reunion.getFecha()), hora.format(reunion.getFecha()), reunion.getLugar(), reunion.getOrden() });
			reunion.setEmail(true);
			reunionesService.insertarModificarReunion(reunion, null, null);
			Mail.send(recipients, subject, message);
			Message.info("meetings.info.mail.success");
		} else {
			Message.error("meetings.error.mail.recipients");
		}
	}

	/**
	 * Valida y guarda los datos de la nueva reunión.
	 */
	public void guardar(String message) {
		if (validar()) {
			SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
			Date date = reunion.getFecha();
			String fecha = sdf.format(date);
			fecha = fecha + " " + hora + ":" + minuto;
			sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm");
			try {
				reunion.setFecha(sdf.parse(fecha));
			} catch (Exception e) {
				reunion.setFecha(date);
			}
			reunionesService.insertarModificarReunion(reunion, asistentes, eliminados);
			load();
			Message.info(message);
		}
	}

	/**
	 * Valida los datos de la reunión y devuelve el resultado de la validación.
	 * 
	 * @return <code>true</code> si la validación es correcta.
	 *         <code>false</code> en caso contrario.
	 */
	private boolean validar() {
		boolean resultado = true;
		if (reunion.getFecha() == null) {
			resultado = false;
			Message.error("meetings.error.meeting.date.null");
		} else if (reunion.getFecha().compareTo(new Date()) < 0) {
			resultado = false;
			Message.error("meetings.error.meeting.date.below");
		}
		if (reunion.getLugar() == null || "".equals(reunion.getLugar())) {
			resultado = false;
			Message.error("meetings.error.meeting.place");
		}
		if (reunion.getOrden() == null || "".equals(reunion.getOrden())) {
			resultado = false;
			Message.error("meetings.error.agenda.null");
		}
		return resultado;
	}

	/**
	 * Carga los datos necesarios para el alta de reunión.
	 */
	public void nueva() {
		reunion = new Reunion();
		hora = "00";
		minuto = "00";
		asistentes = new ArrayList<Usuario>();
		eliminados = new ArrayList<Asistente>();
		anteriores = new ArrayList<Asistente>();
		seleccionados = new HashMap<Usuario, Boolean>();
		listarReuniones = false;
		editarReunion = true;
	}

	/**
	 * Elimina la reunión del listado de reuniones y le pone la fecha actual
	 * como fecha de baja. Si se ha enviado el email de convocatoria, se enviará
	 * un nuevo email para cancelar la reunión.
	 */
	public void eliminar() {
		reunion.setBaja(new Date());
		if (reunion.isEmail()) {
			List<Usuario> convocados = reunionesService.obtenerUsuarios(reunion.getFecha());
			if (convocados != null && !convocados.isEmpty()) {
				List<String> recipients = new ArrayList<String>();
				for (Usuario usuario : convocados) {
					log.info("To: " + usuario.getEmail());
					recipients.add(usuario.getEmail());
				}

				SimpleDateFormat fecha = new SimpleDateFormat("dd/MM/yyyy");
				SimpleDateFormat hora = new SimpleDateFormat("HH:mm");
				String subject = MessageFormat.format(Message.get("mail.meeting.canceled.subject"), new Object[] { fecha.format(reunion.getFecha()) });
				String message = MessageFormat.format(Message.get("mail.meeting.canceled.message"),
						new Object[] { fecha.format(reunion.getFecha()), hora.format(reunion.getFecha()), reunion.getLugar() });
				Mail.send(recipients, subject, message);
			}
		}
		reunionesService.insertarModificarReunion(reunion, null, null);
		load();
	}

	/**
	 * Carga los datos necesarios para la edición de la reunión seleccionada.
	 */
	public void editar() {
		usuarios = reunionesService.obtenerUsuarios(reunion.getFecha());
		seleccionados = new HashMap<Usuario, Boolean>();
		anteriores = new ArrayList<Asistente>();
		if (reunion != null) {
			if (reunion.getFecha() != null) {
				SimpleDateFormat sdf = new SimpleDateFormat("HH");
				hora = Integer.valueOf(sdf.format(reunion.getFecha())).toString();
				sdf = new SimpleDateFormat("mm");
				minuto = Integer.valueOf(sdf.format(reunion.getFecha())).toString();
			}
			if (reunion.getAsistentes() != null && !reunion.getAsistentes().isEmpty()) {
				for (Asistente asistente : reunion.getAsistentes()) {
					seleccionados.put(asistente.getUsuario(), true);
					anteriores.add(asistente);
				}
			}
		}
		listarReuniones = false;
		editarReunion = true;
	}

	public void setReunion(Reunion reunion) {
		this.reunion = reunion;
	}

	public Reunion getReunion() {
		return reunion;
	}

	public boolean isListarReuniones() {
		return listarReuniones;
	}

	public void setListarReuniones(boolean listarReuniones) {
		this.listarReuniones = listarReuniones;
	}

	public boolean isEditarReunion() {
		return editarReunion;
	}

	public void setEditarReunion(boolean editarReunion) {
		this.editarReunion = editarReunion;
	}

	public List<Reunion> getReuniones() {
		return reuniones;
	}

	public void setReuniones(List<Reunion> reuniones) {
		this.reuniones = reuniones;
	}

	public String getHora() {
		return hora;
	}

	public void setHora(String hora) {
		this.hora = hora;
	}

	public String getMinuto() {
		return minuto;
	}

	public void setMinuto(String minuto) {
		this.minuto = minuto;
	}

	public List<SelectItem> getHoras() {
		return horas;
	}

	public void setHoras(List<SelectItem> horas) {
		this.horas = horas;
	}

	public List<SelectItem> getMinutos() {
		return minutos;
	}

	public void setMinutos(List<SelectItem> minutos) {
		this.minutos = minutos;
	}

	public List<Usuario> getUsuarios() {
		return usuarios;
	}

	public void setUsuarios(List<Usuario> usuarios) {
		this.usuarios = usuarios;
	}

	public Map<Usuario, Boolean> getSeleccionados() {
		return seleccionados;
	}

	public void setSeleccionados(Map<Usuario, Boolean> seleccionados) {
		this.seleccionados = seleccionados;
	}

	public void setHoy(Date hoy) {
		this.hoy = hoy;
	}

	public Date getHoy() {
		return hoy;
	}
}
